function ngdomSprite(x, y) {
	if (!ngdom.Engine) return;
	var options = false;
	if (typeof(x) == 'object') {
		options = x;
		x = options.x;
		y = options.y;
	}
	
	this.engine = ngdom.Engine;
	this.el = null;
	this.cssClass = false;
	this.speed = (Math.random() * 1.5) + 0.5;
	this.moveSpeed = this.speed;
	this.runSpeed = this.speed + 1;
	this.running = false;
	this.defaultSpeed = this.speed;
	this.direction = Math.floor(Math.random() * 4) + 1;
	this.collidesWithWalls = true;
	this.reverseOnCollision = true;
	this.reverseCount = 0;
	this.initialPosition = {
		x: x,
		y: y
	};
	this.position = {
		x: x == 'random' ? Math.floor(Math.random() * (this.engine.portalSize.width - 2 - ngdom.Engine.portalEdgeBuffer*2)) + 1 + ngdom.Engine.portalEdgeBuffer : x,
		y: y == 'random' ? Math.floor(Math.random() * (this.engine.portalSize.height - 2 - ngdom.Engine.portalEdgeBuffer*2)) + 1 + ngdom.Engine.portalEdgeBuffer : y
	};
	this.type = 'sprite';
	this.playerOverlap = false;
	this.layer = 2;
	this.zOffset = 0;
	this.sceneDelay = 250;
	this.sceneStartTime = 0;
	this.arriveEarlyStayLate = false;
	this.health = 10;
	this.maxHealth = 10;
	this.invincible = false;
	this.touchDamage = 5;
	this.weapon = null;
	this.attacking = false;
	this.attackLength = 500;
	this.attackStartTime = 0;
	this.attackDirection = 0;
	this.attackTurnDelay = 0;
	this.aimDelay = 0;
	this.hasShield = false;
	this.wounded = false;
	this.woundedLength = 300;
	this.woundedBounceLength = 300;
	this.woundedBounceSpeed = 4;
	this.woundedStartTime = 0;
	this.dying = false;
	this.dyingLength = 500;
	this.dyingStartTime = 0;
	this.dead = false;
	this.worthXP = 3;
	this.canDrop = false;
	this.dropRate = 0.33;
	this.drops = 'ngdomSpriteItem__Health';
	this.sprites = {};
	this.spriteKey = 'default';
	this.spriteExt = 'gif';
	this.spriteStates = (options && options.spriteStates) ? options.spriteStates : ['idle', 'move', 'attack', 'dying'];
	this.spriteDirections = (options && options.spriteDirections) ? options.spriteDirections : ['u', 'r', 'd', 'l'];
	
	if (options) $.extend(true, this, options);
}
ngdomSprite.prototype.enterScene = function () {
	if (this.initialPosition.x == 'random' || this.initialPosition.y == 'random')
	{
		while (this.wallAtPosition() > 0)
		{
			this.position = {
				x: this.initialPosition.x == 'random' ? 
					Math.floor(Math.random() * (this.engine.portalSize.width - 2 - (ngdom.Engine.portalEdgeBuffer * 2)) 
					+ 1 + ngdom.Engine.portalEdgeBuffer) : 
					this.initialPosition.x,
				y: this.initialPosition.y == 'random' ? 
					Math.floor(Math.random() * (this.engine.portalSize.height - 2 - (ngdom.Engine.portalEdgeBuffer * 2)) 
					+ 1 + ngdom.Engine.portalEdgeBuffer) : 
					this.initialPosition.y
			};
		}
	}
	this.el = $('<img class="obj" alt="" />');
	if (this.cssClass) this.el.addClass(this.cssClass);
	if (this.arriveEarlyStayLate) this.el.addClass('early');
	if (this.worthXP == 0) this.el.css('opacity', '0.5');
	if (this.dead) this.el.hide();
	this.sceneStartTime = this.engine.tickCount;
	this.el.appendTo(this.engine.view);
	this.setSprite();
	this.setPosition();
};
ngdomSprite.prototype.exitScene = function () {
	if (this.el) this.el.remove();
	this.el = null;
};
ngdomSprite.prototype.initSprites = function () {
	if (!this.sprites.initialized) {
		var that = this;
		var cacheSprite = function (sprite_id) {
			if (!that.engine.spriteCache[sprite_id]) {
				that.engine.spriteCache[sprite_id] = new Image();
				that.engine.spriteCache[sprite_id].src = that.engine.paths.sprite + sprite_id + '.' + that.spriteExt;
			}
			return sprite_id + '.' + that.spriteExt;
		};
		if (this.spriteStates) {
			$.each(this.spriteStates, function (index, state) {
				if (that.spriteDirections) {
					$.each(that.spriteDirections, function (index, dir) {
						if (typeof(that.sprites[state+'_'+dir]) == 'undefined') {
							var sprite_id = that.spriteKey+'_'+state+'_'+dir;
							that.sprites[state+'_'+dir] = { src: cacheSprite(sprite_id), flip: false, cssClass: '' };
						}
					});
					
					if (that.sprites[state+'_u'] && !that.sprites[state+'_d']) 
						that.sprites[state+'_d'] = $.extend({}, that.sprites[state+'_u'], { flip: true });
					if (that.sprites[state+'_d'] && !that.sprites[state+'_u']) 
						that.sprites[state+'_u'] = $.extend({}, that.sprites[state+'_d'], { flip: true });
					if (that.sprites[state+'_l'] && !that.sprites[state+'_r']) 
						that.sprites[state+'_r'] = $.extend({}, that.sprites[state+'_l'], { flip: true });
					if (that.sprites[state+'_r'] && !that.sprites[state+'_l']) 
						that.sprites[state+'_l'] = $.extend({}, that.sprites[state+'_r'], { flip: true });
				}
				else {
					if (typeof(that.sprites[state]) == 'undefined') 
						that.sprites[state] = { src: cacheSprite(that.spriteKey+'_'+state), flip: false, cssClass: '' };
				}
			});
		}
		else {
			this.sprites.main = { src: cacheSprite(this.spriteKey), flip: false, cssClass: '' };
		}
		this.sprites.initialized = true;
	}
};
ngdomSprite.prototype.setSprite = function () {
	if (typeof(this.sprites.main) != 'undefined')
	{
		this.activateSprite(this.sprites.main, false);
		return;
	}
	if ((this.dying || this.dead) && (typeof(this.sprites.dying) != 'undefined' || typeof(this.sprites.dying_l) != 'undefined'))
	{
		if (typeof(this.sprites.dying) != 'undefined') 
			this.activateSprite(this.sprites.dying, false);
		else if (typeof(this.sprites.dying_u) != 'undefined' && this.direction == 1) 
			this.activateSprite(this.sprites.dying_u, false);
		else if (typeof(this.sprites.dying_r) != 'undefined' && this.direction == 2) 
			this.activateSprite(this.sprites.dying_r, false);
		else if (typeof(this.sprites.dying_d) != 'undefined' && this.direction == 3) 
			this.activateSprite(this.sprites.dying_d, false);
		else if (typeof(this.sprites.dying_l) != 'undefined' && this.direction == 4) 
			this.activateSprite(this.sprites.dying_l, false);
		else if (typeof(this.sprites.dying_l) != 'undefined' && this.direction == 1) 
			this.activateSprite(this.sprites.dying_l, false);
		else if (typeof(this.sprites.dying_r) != 'undefined' && this.direction == 3) 
			this.activateSprite(this.sprites.dying_r, false);
			
		return;
	}
	if (this.direction == 1) 
	{
		if (this.attacking && typeof(this.sprites.attack_u) != 'undefined')
			this.activateSprite(this.sprites.attack_u, true);
		else if (this.speed == 0 && typeof(this.sprites.idle_u) != 'undefined')
			this.activateSprite(this.sprites.idle_u, true);
		else
			this.activateSprite(this.sprites.move_u, true);
	}
	if (this.direction == 2) 
	{
		if (this.attacking && typeof(this.sprites.attack_r) != 'undefined')
			this.activateSprite(this.sprites.attack_r, false);
		else if (this.speed == 0 && typeof(this.sprites.idle_r) != 'undefined')
			this.activateSprite(this.sprites.idle_r, false);
		else
			this.activateSprite(this.sprites.move_r, false);
	}
	if (this.direction == 3) 
	{
		if (this.attacking && typeof(this.sprites.attack_d) != 'undefined')
			this.activateSprite(this.sprites.attack_d, true);
		else if (this.speed == 0 && typeof(this.sprites.idle_d) != 'undefined')
			this.activateSprite(this.sprites.idle_d, true);
		else
			this.activateSprite(this.sprites.move_d, true);
	}
	if (this.direction == 4) 
	{
		if (this.attacking && typeof(this.sprites.attack_l) != 'undefined')
			this.activateSprite(this.sprites.attack_l, false);
		else if (this.speed == 0 && typeof(this.sprites.idle_l) != 'undefined')
			this.activateSprite(this.sprites.idle_l, false);
		else
			this.activateSprite(this.sprites.move_l, false);
	}
};
ngdomSprite.prototype.activateSprite = function (sprite, vertical) {
	if (!sprite || !this.el) return;
	this.el.removeClass('vflip').removeClass('hflip');
	if (this.el.data('spriteCssClass')) this.el.removeClass(this.el.data('spriteCssClass'));
	if (sprite.cssClass != '') this.el.addClass(sprite.cssClass).data('spriteCssClass', sprite.cssClass);
	if (this.el.attr('src') != this.engine.paths.sprite + sprite.src) this.el.attr('src', this.engine.paths.sprite + sprite.src); 
	if (sprite.flip) this.el.addClass(vertical ? 'vflip' : 'hflip');
};
ngdomSprite.prototype.setPosition = function () {
	if (!this.el) return;
	this.el.css({ 
		'left': this.position.x+'px',
		'top': this.position.y+'px',
		'z-index': this.position.y + this.zOffset + this.layer * 1000
	});
};
ngdomSprite.prototype.getHitBox = function () {
	if (this.direction == 1 && typeof(this.sprites.attack_u) != 'undefined' && typeof(this.sprites.attack_u.hitBox) != 'undefined') return this.sprites.attack_u.hitBox;
	else if (this.direction == 2 && typeof(this.sprites.attack_r) != 'undefined' && typeof(this.sprites.attack_r.hitBox) != 'undefined') return this.sprites.attack_r.hitBox;
	else if (this.direction == 3 && typeof(this.sprites.attack_d) != 'undefined' && typeof(this.sprites.attack_d.hitBox) != 'undefined') return this.sprites.attack_d.hitBox;
	else if (this.direction == 4 && typeof(this.sprites.attack_l) != 'undefined' && typeof(this.sprites.attack_l.hitBox) != 'undefined') return this.sprites.attack_l.hitBox;
	else return false;
};
ngdomSprite.prototype.wallAtPosition = function (position, walls) {
	if (typeof(position) == 'undefined') position = this.position;
	if (typeof(walls) == 'undefined') walls = this.engine.map.grid[this.engine.map.position.x][this.engine.map.position.y].walls;
	return walls[Math.floor(position.x / ngdom.Engine.wallGridScale) + (Math.floor(position.y / ngdom.Engine.wallGridScale) * ngdom.Engine.wallGridSize.width)];
};
ngdomSprite.prototype.attack = function (direction) {
	if (this.dead || this.dying || this.wounded) return;

	this.attacking = true;
	this.attackStartTime = this.engine.tickCount;
	if (typeof(direction) != 'undefined') this.attackDirection = direction;

	if (this.weapon && this.weapon.isRanged)
		this.weapon.fire();
};
ngdomSprite.prototype.loop = function () {
	if (this.attacking)
	{
		if (Math.abs(this.direction - this.attackDirection) == 2 && this.attackTurnDelay >= 0) {
			if (this.engine.tickCount - this.attackStartTime > this.attackTurnDelay) {
				this.direction++;
				if (this.direction > 4) this.direction = 1;
				this.attackStartTime += this.attackTurnDelay;
			}
		}
		else if (Math.abs(this.direction - this.attackDirection) == 1 && this.attackTurnDelay >= 0) {
			if (this.engine.tickCount - this.attackStartTime > this.attackTurnDelay) {
				this.direction = this.attackDirection;
				this.attackStartTime += this.attackTurnDelay;
			}
		}
		else {
			if (this.engine.tickCount - this.attackStartTime > this.attackLength) {
				this.attacking = false;
				this.speed = this.defaultSpeed;
			}
			else if (this.weapon) {
				this.weapon.loop();
			}
		}
	}
	if (this.wounded)
	{
		if (this.engine.tickCount - this.woundedStartTime > this.woundedLength) {
			this.wounded = false;
		}
		if (this.engine.tickCount - this.woundedStartTime > this.woundedBounceLength && this.speed < 0) {
			this.speed = this.defaultSpeed;
		}
	}
	if (this.dying && this.type != 'player')
	{
		if (this.engine.tickCount - this.dyingStartTime > this.dyingLength) {
			this.die();
		}
	}
}
ngdomSprite.prototype.behave = function () {
	if (this.dead || this.dying || this.wounded) return;
	if (Math.floor(Math.random() * 50) == 7) //1 in 50 chance to change direction
	{
		this.direction = Math.floor(Math.random() * 4) + 1;
	}
};
ngdomSprite.prototype.move = function (walls) {
	if (this.dead || this.dying) return;
	if (!this.attacking)
	{
		if (typeof(walls) == 'undefined') walls = this.engine.map.grid[this.engine.map.position.x][this.engine.map.position.y].walls;
	
		//directions: 1 = up; 2 = right; 3 = down; 4 = left
		if (this.direction == 1) { this.position.y -= this.speed; if (this.position.y < 0 && this.reverseOnCollision) this.direction = 3; }
		if (this.direction == 2) { this.position.x += this.speed; if (this.position.x > ngdom.Engine.portalSize.width && this.reverseOnCollision) this.direction = 4; }
		if (this.direction == 3) { this.position.y += this.speed; if (this.position.y > ngdom.Engine.portalSize.height && this.reverseOnCollision) this.direction = 1; }
		if (this.direction == 4) { this.position.x -= this.speed; if (this.position.x < 0 && this.reverseOnCollision) this.direction = 2; }
	
		var loopCheck = 0;
		var collision = false;
		if (this.collidesWithWalls
			&& this.position.x >= 0 && this.position.x < ngdom.Engine.portalSize.width 
			&& this.position.y >= 0 && this.position.y < ngdom.Engine.portalSize.height)
		{
			while (this.wallAtPosition(this.position, walls) > 0 && loopCheck < 1200)
			{
				loopCheck++;
				collision = true;
				if ((this.direction == 1 && this.speed >= 0) || (this.direction == 3 && this.speed < 0)) //up
				{
					this.position.y = (Math.floor(this.position.y / ngdom.Engine.wallGridScale) + 1) * ngdom.Engine.wallGridScale;
					if (this.position.y < 0) { this.position.y = 8; this.direction = this.oppositeDirection(); }
				}
				else if ((this.direction == 3 && this.speed >= 0) || (this.direction == 1 && this.speed < 0)) //down
				{
					this.position.y = Math.floor(this.position.y / ngdom.Engine.wallGridScale) * ngdom.Engine.wallGridScale - 1;
					if (this.position.y >= ngdom.Engine.portalSize.height) { this.position.y = ngdom.Engine.portalSize.height - 9; this.direction = this.oppositeDirection(); }
				}
				else if ((this.direction == 2 && this.speed >= 0) || (this.direction == 4 && this.speed < 0)) //right
				{
					this.position.x = Math.floor(this.position.x / ngdom.Engine.wallGridScale) * ngdom.Engine.wallGridScale - 1;
					if (this.position.x >= ngdom.Engine.portalSize.width) { this.position.x = ngdom.Engine.portalSize.width - 9; this.direction = this.oppositeDirection(); }
				}
				else if ((this.direction == 4 && this.speed >= 0) || (this.direction == 2 && this.speed < 0)) //left
				{
					this.position.x = (Math.floor(this.position.x / ngdom.Engine.wallGridScale) + 1) * ngdom.Engine.wallGridScale;
					if (this.position.x < 0) { this.position.x = 8; this.direction = this.oppositeDirection(); }
				}
			}
			if (collision) this.collideWithWall();
		}
	}
	
	if (this.type != 'player')
	{
		//collide with player's fired shots to receive weapon damage
		if (!this.invincible && !this.wounded)
		{
			for (var i=0; i<this.engine.objects.length; i++) {
				var object = this.engine.objects[i];
				if (!object.dead && object.type == 'weapon' && object.owner && object.owner.type == 'player'
				  && this.position.y < object.position.y + 12 && this.position.y > object.position.y - 12 
				  && this.position.x > object.position.x - 12 && this.position.x < object.position.x + 12)
				{
					this.collideWithPlayerWeapon(object);
					break;
				}	
			}
		}

		//collide with player to give touch damage or activate items
		if (!this.engine.player.wounded && this.overlapsWithPlayer()) {
			this.collideWithPlayer();
			this.playerOverlap = true;
		}
		else
			this.playerOverlap = false;
	}

	this.setSprite();
	this.setPosition();
};
ngdomSprite.prototype.overlapsWithPlayer = function () {
	return (
		this.position.y < ngdom.Engine.player.position.y + 12 && this.position.y > ngdom.Engine.player.position.y - 12 
	 && this.position.x > ngdom.Engine.player.position.x - 12 && this.position.x < ngdom.Engine.player.position.x + 12
	 );
};
ngdomSprite.prototype.collideWithWall = function () {
	if (this.reverseOnCollision) this.reverseDirection();
};
ngdomSprite.prototype.collideWithPlayer = function () {
	this.reverseDirection();
	this.engine.player.damage(this.touchDamage, this.direction);
};
ngdomSprite.prototype.collideWithPlayerWeapon = function (weapon) {
	if (this.canBlockWeapon(weapon)) 
	{
		if (!weapon.blockBounceTime || this.engine.tickCount - weapon.blockBounceTime > 200) {
			weapon.reverseDirection();
			weapon.blockBounceTime = this.engine.tickCount;
			this.engine.sounds.play('block');
		}
	}
	else {
		weapon.die();
		this.damage(weapon.touchDamage);
	}
};
ngdomSprite.prototype.canBlockWeapon = function (weapon) {
	return false;
};
ngdomSprite.prototype.damage = function (amount) {
	if (this.invincible) return;
	this.health -= amount;
	if (this.health <= 0)
	{
		ngdom.Engine.sounds.play('kill');
		ngdom.Engine.player.gainXP(this.worthXP);
		this.worthXP = 0;
		this.attacking = false;
		this.dying = true;
		this.dyingStartTime = this.engine.tickCount;
	}
	else
	{
		ngdom.Engine.sounds.play('hit');
		this.wounded = true;
		this.woundedStartTime = this.engine.tickCount;
		this.direction = this.engine.player.direction;
		this.reverseDirection();
		this.speed = -this.woundedBounceSpeed;
		this.showDamage();
	}
};
ngdomSprite.prototype.die = function () {
	this.dying = false;
	this.dead = true;
	this.el.remove();
	if (this.canDrop) this.dropItem();
};
ngdomSprite.prototype.oppositeDirection = function () {
	if (this.direction == 1) return 3;
	else if (this.direction == 3) return 1;
	else if (this.direction == 2) return 4;
	else if (this.direction == 4) return 2;
};
ngdomSprite.prototype.reverseDirection = function () {
	this.direction = this.oppositeDirection();

	this.reverseCount++;
	if (this.reverseCount > 4)
	{
		this.direction++;
		if (this.direction > 4) this.direction = 1;
		this.reverseCount = 0;
	}
};
ngdomSprite.prototype.showDamage = function () {
	for (var i = 0; i < this.woundedLength; i += 200) {
		this.el.fadeTo(20, 0.4).delay(80).fadeTo(20, 1).delay(80);
	}
	if (this.worthXP == 0) this.el.fadeTo(20, 0.5);
};
ngdomSprite.prototype.resurrect = function () {
	this.health = this.maxHealth;
	this.dead = false;
};
ngdomSprite.prototype.dropItem = function () {
	if (Math.random() <= this.dropRate)
	{
		var drop = $.isArray(this.drops) ? this.drops[Math.round(Math.random() * (this.drops.length - 1))] : this.drops;
		var item = new window[drop](this.position.x, this.position.y);
		item.initSprites();
		item.enterScene();
		this.engine.addProjectile(item);
	}
};
ngdomSprite.prototype.save = function () {
	return {};
};
ngdomSprite.prototype.load = function (data) {
};
ngdomSprite.prototype.reset = function () {
};